#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <unordered_map>

#include "Log.hpp"
#include "Socket.hpp"
#include "TcpServer.hpp"

const std::string http_sep = "\r\n"; 
const std::string webRoot = "./bili/"; 
const std::string header_sep = ": "; 


// get / http/1.1
class HttpRequest
{
private:
    std::string GetOneLine(std::string& message)
    {
        size_t pos = message.find(http_sep);
        if(pos == std::string::npos) return "";

        std::string ret = message.substr(0, pos);
        message.erase(0, pos+2);

        return ret;
    }
public:
    HttpRequest()
    {}

    // /a/b/c.html
    std::string GetUrl()
    {
        std::string path = webRoot;
        if(_url == "/")
        {
            path += "index.html";
        }
        else
        {
            path += _url;
        }
        return path;
    }

    std::string GetSuffix()
    {
        _suffix = ".html";

        size_t pos = _url.rfind('.');
        if(pos == std::string::npos) return _suffix;

        _suffix = _url.substr(pos);
        return _suffix;
    }

    void Deserialize(std::string& message)
    {
        _fline = GetOneLine(message);

        // handle first line
        std::stringstream ss(_fline);
        ss >> _method >> _url >> _version;

        while(true)
        {
            std::string line = GetOneLine(message);
            if(line == "") break;
            _header += line;
        }

        _text = message;
    }

    void print()
    {
        printf("method: %s, path: %s, version: %s\n", _method.c_str(), _url.c_str(), _version.c_str());
        printf("====================================\n");
        std::cout << _header << std::endl;
        std::cout << _text << std::endl;
    }

    std::string _method;
    std::string _url;
    std::string _version;

    std::string _suffix;

    std::string _fline;
    std::string _header;
    std::string _text;
};

// HTTP/1.0 200 OK
class HttpResponse
{
private:
public:
    HttpResponse()
    {}

    void AddHeader(std::string k, std::string v)
    {
        std::string line = k + header_sep + v;
        _header += line;
        _header += http_sep;
    }

    std::string FillText(std::string path)
    {
        std::ifstream in(path, std::ios::binary);
        if(!in.is_open()) 
        {
            LOG(ERROR, "open file %s failed\n", path.c_str());
            return "";
        }

        in.seekg(0, std::ios::end);
        size_t length = in.tellg();
        in.seekg(0, std::ios::beg);

        _text.resize(length);
        in.read((char*)_text.c_str(), length);
        return _text;
    }

    std::string Serialize()
    {
        _version = "HTTP/1.1";
        _code = "200";
        _desc = "OK";

        _fline = "HTTP/1.0 200 OK";

        std::string message = _fline + http_sep + _header + http_sep + _text;
        return message;
    }
    
private:
    std::string _version;
    std::string _code;
    std::string _desc;

    std::string _fline;
    std::string _header;
    std::string _text;
};

class HttpServer
{
public:
    HttpServer()
    {
        _ctTable.insert(std::make_pair(".html", "text/html"));
        _ctTable.insert(std::make_pair(".jpg", "application/x-jpg"));
        _ctTable.insert(std::make_pair(".img", "application/x-img"));
    }
    void HttpHelper(std::shared_ptr<sock::TcpSocket> ts, InetAddr client)
    {
        std::string message;
        
        ssize_t n = ts->Recv(&message);
        std::cout << message << std::endl;

        HttpRequest hreq;
        HttpResponse hres;
        hreq.Deserialize(message);

        std::string url = hreq.GetUrl();
        std::cout << "url ==================\n" << url << std::endl;
        std::string text = hres.FillText(url);

        std::cout << "text ==================\n" << text << std::endl;
        hres.AddHeader("Content-Length", std::to_string(text.size()));
        std::string suffix = hreq.GetSuffix();
        hres.AddHeader("Content-Type", _ctTable[suffix]);
        std::cout << "suffix ==================\n" << suffix << std::endl;



        ts->Send(hres.Serialize());
        // ts->Send("HELLO");
    }

private:
    std::unordered_map<std::string, std::string> _ctTable;

};